grammar BasicRegexp;

@parser::header {
package com.taobao.lottery.util.regexp.antlr;

import java.util.LinkedList;
import com.taobao.lottery.util.regexp.MetaGen;
import com.taobao.lottery.util.regexp.util.GenContext;
import com.taobao.lottery.util.regexp.metagen.*;
}
@lexer::header {
package com.taobao.lottery.util.regexp.antlr;
}

parse[GenContext ctx] returns [MetaGen meta]
	scope {
		GenContext int_ctx;
		int idx_cnt;
	}
	@init {
		$parse::int_ctx = $ctx;
		$parse::idx_cnt = 1;
	}
	:	regex EOF
	{ $meta = $regex.meta; }
	;

regex returns [MetaGen meta]
	@init {
		LinkedList<MetaGen> metas = new LinkedList<MetaGen>();
	}
	:
	a1=seqAtoms { metas.add($a1.meta); } (Or a2=seqAtoms { metas.add($a2.meta); })*
	{
		if (metas.size() >= 2) {
			$meta = new Union(metas);
		} else {
			$meta = metas.get(0);
		}
	}
	;

seqAtoms returns [MetaGen meta]
	@init {
		LinkedList<MetaGen> metas = new LinkedList<MetaGen>();
	}
	:	(	a1=singularRegexAtom { metas.add($a1.meta); }
			(	Star { metas.add(new Star(metas.removeLast())); }
			|	Plus { metas.add(new Plus(metas.removeLast())); }
			|	QuestionMark { metas.add(new Opt(metas.removeLast())); }
			|	CurlyBracketStart tm1=Decimal CurlyBracketEnd {
					int m1 = Integer.parseInt($tm1.text);
					metas.add(new NRep(metas.removeLast(), m1, m1));
				}
			|	CurlyBracketStart tm2=Decimal Comma CurlyBracketEnd {
					int m2 = Integer.parseInt($tm2.text);
					metas.add(new NRep(metas.removeLast(), m2, 0));
				}
			|	CurlyBracketStart tm3=Decimal Comma tn3=Decimal CurlyBracketEnd {
					int m3 = Integer.parseInt($tm3.text);
					int n3 = Integer.parseInt($tn3.text);
					metas.add(new NRep(metas.removeLast(), m3, n3));
				}
			)?
		)+
		{
			if (metas.size() >= 2) {
				$meta = new Concat(metas);
			} else {
				$meta = metas.get(0);
			}
		}
	;

singularRegexAtom returns [MetaGen meta]
	:	(a1=characterClass { $meta = $a1.meta; })
	|	(a2=nonCapturingGroup { $meta = $a2.meta; })
	|	(a3=generator { $meta = $a3.meta; })
	|	(a4=group { $meta = $a4.meta; })
	|	(a5=backReference { $meta = $a5.meta; })
	|	(a6=literalChars { $meta = $a6.meta; })
	|	(Dot { $meta = new Dot(); })
	|	(t7=ShortCharClass { $meta = new Charset($t7.text); })
	;

// \1, \2, ...
backReference returns [MetaGen meta]
	:	Escape t=Decimal
	{ $meta = new Ref($parse::int_ctx, Integer.parseInt($t.text)); }
	;

// (...)
group returns [MetaGen meta]
	:	RoundBracketStart r=regex RoundBracketEnd
	{
		$meta = new Capture($parse::int_ctx, $parse::idx_cnt, $r.meta);
		$parse::idx_cnt++;
	}
	;

// (?:...)
nonCapturingGroup returns [MetaGen meta]
	:	RoundBracketStart QuestionMark Colon r=regex RoundBracketEnd
	{ $meta = $r.meta; }
	;

// (?{name}), (?{name(arg1, arg2, ...)})
generator returns [MetaGen meta]
	@init {
		String name = null;
		List<String> args = new LinkedList<String>();
	}
	:	RoundBracketStart QuestionMark CurlyBracketStart
		(method=genName { name = $method.text; })
		(
			RoundBracketStart
			(arg=genArg { args.add($arg.text); })
			(Comma (arg=genArg { args.add($arg.text); }))*
			RoundBracketEnd
		)?
		CurlyBracketEnd RoundBracketEnd
	{ $meta = new Eval($parse::int_ctx, name, args); }
	;

genName	:	(UpperAlpha | LowerAlpha | UnderScore) (UpperAlpha | LowerAlpha | UnderScore | Digit)*
	;

genArg	:	~(Comma | RoundBracketEnd)+
	;

// [...]
characterClass returns [MetaGen meta]
	:	SquareBracketStart t=characterClassContent SquareBracketEnd
	{ $meta = new Charset($t.text); }
	;

literalChars returns [MetaGen meta]
	:
	(
		(	t=And
		|	t=Hyphen
		|	t=Colon
		|	t=Equals
		|	t=LessThan
		|	t=GreaterThan
		|	t=ExclamationMark
		|	t=Comma
		|	t=Decimal
		|	t=UpperAlpha
		|	t=LowerAlpha
		|	t=UnderScore
		|	t=OtherChar
		)
		{ $meta = new Literal($t.text); }
	)
	|
	(	t=EscapeSequence
		{ $meta = new Literal($t.text.substring(1)); }
	)
	;

characterClassContent
	:	Caret?
	(	ShortCharClass
	|	rangeAtom (Hyphen rangeAtom)?
	)+
	;

rangeAtom
	:	And
	|	Or
	|	Colon
	|	Dollar
	|	RoundBracketStart
	|	RoundBracketEnd
	|	CurlyBracketStart
	|	CurlyBracketEnd
	|	Equals
	|	LessThan
	|	GreaterThan
	|	ExclamationMark
	|	Comma
	|	Plus
	|	Star
	|	QuestionMark
	|	Dot
	|	Decimal
	|	UpperAlpha
	|	LowerAlpha
	|	UnderScore
	|	OtherChar
	;

Decimal	:	Digit+
	;

fragment
Digit			:	'0'..'9' ;

EscapeSequence
	:	Escape ~(LowerAlpha | UpperAlpha | Digit)
	;

LowerAlpha		:	'a'..'z' ;
UpperAlpha		:	'A'..'Z' ;
UnderScore		:	'_' ;

Plus			:	'+'      ;
Star			:	'*'      ;
Or			:	'|'      ;
Dot			:	'.'      ;
SquareBracketStart	:	'['      ;
SquareBracketEnd	:	']'      ;
RoundBracketStart	:	'('      ;
RoundBracketEnd		:	')'      ;
CurlyBracketStart	:	'{'      ;
CurlyBracketEnd		:	'}'      ;
Comma			:	','      ;

Escape			:	'\\'	 ;
ShortCharClass
	:	Escape ('d'|'D'|'s'|'S'|'w'|'W')
	;

And			:	'&'      ;
Hyphen			:	'-'      ;
Caret			:	'^'      ;
Colon			:	':'      ;
Dollar			:	'$'      ;
Equals			:	'='      ;
LessThan		:	'<'      ;
GreaterThan		:	'>'      ;
ExclamationMark		:	'!'      ;
QuestionMark		:	'?'      ;

OtherChar	:
	~(	Escape
	|	And
	|	Or
	|	Hyphen
	|	Caret
	|	Colon
	|	Dollar
	|	SquareBracketStart
	|	SquareBracketEnd
	|	RoundBracketStart
	|	RoundBracketEnd
	|	CurlyBracketStart
	|	CurlyBracketEnd
	|	Equals
	|	LessThan
	|	GreaterThan
	|	ExclamationMark
	|	Comma
	|	Plus
	|	Star
	|	QuestionMark
	|	Dot
	|	Digit
	|	UpperAlpha
	|	LowerAlpha
	|	UnderScore
	)
	;